#include "Socket.hpp"
#include "InetAddr.hpp"
#include <sys/socket.h>
#include <memory>
using namespace socket_ns;

class SelectServer
{
    const static int N = sizeof(fd_set) * 8;
    const static int defaultFd = -1;

public:
    SelectServer(uint16_t port)
        : _port(port), _listenSock(std::make_unique<TcpSocket>())
    {
        // 创建tcp服务端
        _listenSock->BuildListenSocket(InetAddr(port, "0"));

        // 初始化fd辅助数组
        for (int i = 0; i < N; i++)
            _fdArr[i] = defaultFd;
        _fdArr[0] = _listenSock->Sockfd();
    }
    void AcceptClient()
    {
        InetAddr clientAddr;
        int newfd = _listenSock->Accepter(clientAddr); // 这里一定不会被阻塞
        LOG(INFO, "get new link, fd : %d, addr : [%s:%d]\n", newfd, clientAddr.IP().c_str(), clientAddr.Port());

        int idx = 0;
        for (; idx < N; idx++)
            if(_fdArr[idx] == defaultFd)
                break;
        if(idx == N)
        {
            LOG(ERROR, "select server is full\n");
            ::close(newfd);
            return;
        }
        else
        {
            _fdArr[idx] = newfd;
            LOG(INFO, "%d add to select array!\n", newfd);
        }
        LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
    }
    void ServiceIO(int pos)
    {
        int fd = _fdArr[pos];
        char buff[1024];
        ssize_t n = ::read(fd, buff, sizeof(buff) - 1);
        if(n > 0)
        {
            buff[n] = 0;
            std::cout << "client say#" << buff << std::endl;
            std::string message =  buff;
            ::send(fd, message.c_str(), message.size(), 0);
        }
        else if(n == 0)
        {
            LOG(INFO, "%d is closed\n", fd);
            ::close(fd);
            _fdArr[pos] = defaultFd;
            LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
        }
        else
        {
            LOG(ERROR, "recv error\n");
            ::close(fd);
            _fdArr[pos] = defaultFd;
            LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
        }
    }
    void HandlerEvent(fd_set &s)
    {
        for (int i = 0; i < N; i++)
        {
            if(_fdArr[i] != defaultFd && FD_ISSET(_fdArr[i], &s))
            {
                if(_fdArr[i] == _listenSock->Sockfd())
                {
                    AcceptClient();
                }
                else
                {
                    ServiceIO(i);
                }
            }
        }
    }
    void Loop()
    {
        while (true)
        {
            fd_set read;
            FD_ZERO(&read);
            int maxfd = -1;
            for (int i = 0; i < N; i++)
            {
                if (_fdArr[i] != defaultFd)
                {
                    FD_SET(_fdArr[i], &read);
                    if (_fdArr[i] > maxfd)
                        maxfd = _fdArr[i];
                }
            }

            struct timeval tm = {0, 0};
            int n = select(maxfd + 1, &read, nullptr, nullptr, /*&tm*/ nullptr);
            switch (n)
            {
            case 0:
                LOG(INFO, "time out.... %d.%d\n", tm.tv_sec, tm.tv_usec);
                break;
            case -1:
                LOG(ERROR, "select error\n");
                break;
            default:
                HandlerEvent(read);
                break;
            }
        }
    }
    std::string GetStringFd()
    {
        std::string ret;
        for (int i = 0; i < N; i++)
            if(_fdArr[i] != defaultFd)
                ret += std::to_string(_fdArr[i]) + "  ";
        return ret;
    }
private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;

    int _fdArr[N];
};
